using System;
using Microsoft.Practices.EnterpriseLibrary.Caching;
using Microsoft.Practices.EnterpriseLibrary.Caching.Expirations;
using NWrapper.Cache;
using NWrapper.Config;

namespace NWrapper.Cache.EnterpriseLibrary
{
	/// <summary>
	/// Implementation of a cache that uses the Microsoft Enterprise Library's Caching Application Block (CAB).
	/// Versions 2.0 and 3.0 of the CAB have been tested; future versions should work so long as the API 
	/// remains unchanged.
	/// 
	/// Using this library requires the inclusion of the following assemblies:
	/// Microsoft.Practices.EnterpriseLibrary.Caching, 
	/// Microsoft.Practices.EnterpriseLibrary.Common, and
	/// Microsoft.Practices.ObjectBuilder.
	/// </summary>
	/// <remarks>
	/// Configuration settings can be applied by using the custom section of your configuration file.
	/// See the <see cref="Initialize()"/> method for more details.
	/// </remarks>
	public class EnterpriseLibraryCache : ICache, IDisposable
	{
		private CacheManager _cache;

		/// <summary>
		/// Default constructor.
		/// </summary>
		public EnterpriseLibraryCache()
		{
		}

		#region ICache Members
		/// <summary>
		/// Initializes the underlying cache.
		/// </summary>
		/// <remarks>
		/// Custom settings can be set to configure the Enterprise Library Caching library, where:
		/// 
		/// <list>
		/// <item><c>cacheManagerName</c> is the name of the CacheManager to load (default: <c>NWrapper</c>)</item>
		/// </list>
		/// 
		/// <code>
		/// <![CDATA[
		/// <NWrapper logger="None">
		///   <cache type="EnterpriseLibrary">
		///     <custom>
		///       <add key="cacheManagerName" value="NWrapper" />
		///     </custom>
		///   </cache>
		/// </NWrapper>
		/// ]>
		/// </code>
		/// </remarks>
		/// <param name="settings">The cache settings.</param>
		public void Initialize(CacheSettings settings)
		{
			string cacheManagerName = (settings.Custom["cacheManagerName"] != null ? settings.Custom["cacheManagerName"].Value as string : "NWrapper");

			_cache = CacheFactory.GetCacheManager(cacheManagerName);
		}

		/// <summary>
		/// Gets or sets the cache item with the specified object key.
		/// </summary>
		/// <param name="key">The key of the cache item.</param>
		/// <returns>The cache item's value.</returns>
		public object this[object key]
		{
			get { return this.Get(key); }
			set { this.Set(key, value); }
		}

		/// <summary>
		/// Retrieves an object from the cache.
		/// </summary>
		/// <param name="key">The key of the object.</param>
		/// <returns>The cached object.</returns>
		public object Get(object key)
		{
			if (key == null)
				return null;

			return _cache.GetData(CacheHelper.GetCacheKey(key));
		}

		/// <summary>
		/// Places an item in the cache.
		/// </summary>
		/// <param name="key">The key of the object.</param>
		/// <param name="value">The cached object.</param>
		public void Set(object key, object value)
		{
			if (key == null)
				throw new ArgumentNullException("key");

			_cache.Add(CacheHelper.GetCacheKey(key), value);
		}

		/// <summary>
		/// Place an item in the cache that will expire after the given time span.
		/// </summary>
		/// <param name="key">The key of the object.</param>
		/// <param name="value">The cached object.</param>
		/// <param name="timeToExpire">The <see cref="TimeSpan"/> to use for
		/// item expiration.</param>
		public void Set(object key, object value, TimeSpan timeToExpire)
		{
			if (key == null)
				throw new ArgumentNullException("key");

			_cache.Add(CacheHelper.GetCacheKey(key), value, CacheItemPriority.Normal, null, 
				new AbsoluteTime(DateTime.Now.Add(timeToExpire)));
		}

		/// <summary>
		/// Place an item in the cache with the specified cache expiration.
		/// </summary>
		/// <param name="key">The key of the object.</param>
		/// <param name="value">The cached object.</param>
		/// <param name="expiration">The <see cref="DateTime"/> to use for
		/// item expiration.</param>
		public void Set(object key, object value, DateTime expiration)
		{
			if (key == null)
				throw new ArgumentNullException("key");

			_cache.Add(CacheHelper.GetCacheKey(key), value, CacheItemPriority.Normal, null,
				new AbsoluteTime(expiration));
		}

		/// <summary>
		/// Determines whether an object with the given key exists in the cache.
		/// If the ASP.Net cache is unavailable, this will always return false.
		/// </summary>
		/// <param name="key">The key of the object.</param>
		/// <returns>True if the key exists in the cache, false otherwise.</returns>
		public bool Contains(object key)
		{
			return (this.Get(key) != null);
		}

		/// <summary>
		/// Removes an object from the cache. If the ASP.Net cache is unavailable, this
		/// will do nothing.
		/// </summary>
		/// <param name="key">The key of the object.</param>
		public void Remove(object key)
		{
			if (key != null)
			{
				_cache.Remove(CacheHelper.GetCacheKey(key));
			}
		}

		/// <summary>
		/// Removes all objects from the cache. If the ASP.Net cache is unavailable, this
		/// will do nothing.
		/// </summary>
		public void Clear()
		{
			_cache.Flush();
		}
		#endregion

		#region IDisposable Members
		/// <summary>
		/// Disposes the underlying cache.
		/// </summary>
		public void Dispose()
		{
			Dispose(true);
			GC.SuppressFinalize(true);
		}
		/// <summary>
		/// Disposes the underlying cache.
		/// </summary>
		/// <param name="disposing">Identifies whether the object is being disposed.</param>
		protected virtual void Dispose(bool disposing)
		{
			if (disposing)
			{
				_cache.Dispose();
				_cache = null;
			}
		}
		#endregion
	}
}
